package me.acomma.groot.application.menu;

import me.acomma.groot.domain.menu.Menu;
import me.acomma.groot.domain.menu.MenuId;
import me.acomma.groot.domain.menu.MenuRepository;
import me.acomma.groot.domain.role.RoleId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;

@Component
public class MenuApplicationService {
    @Autowired
    private MenuRepository menuRepository;

    @Autowired
    private MenuAssembler menuAssembler;

    @Transactional(rollbackFor = {Exception.class})
    public void create(CreateMenuCommand command) {
        Menu menu = new Menu();
        menu.setMenuId(menuRepository.nextId());
        menu.setParentId(MenuId.of(command.getParentId()));
        menu.setName(command.getName());
        menu.setIcon(command.getIcon());
        menu.setPath(command.getPath());
        menu.setSortOrder(command.getSortorder());
        menuRepository.add(menu);
    }

    @Transactional(rollbackFor = {Exception.class})
    public void modify(ModifyMenuCommand command) {
        Menu menu = menuRepository.get(MenuId.of(command.getMenuId()));
        if (Objects.isNull(menu)) {
            return;
        }
        menu.setParentId(MenuId.of(command.getParentId()));
        menu.setName(command.getName());
        menu.setIcon(command.getIcon());
        menu.setPath(command.getPath());
        menu.setSortOrder(command.getSortOrder());
        menuRepository.update(menu);
    }

    @Transactional(rollbackFor = {Exception.class})
    public void remove(RemoveMenuCommand command) {
        MenuId menuId = MenuId.of(command.getMenuId());
        long count = menuRepository.countByParentId(menuId);
        if (count > 0) {
            throw new IllegalStateException("存在下级菜单");
        }
        Menu menu = menuRepository.get(menuId);
        if (Objects.isNull(menu)) {
            return;
        }
        menuRepository.remove(menu);
    }

    public MenuData get(Long menuId) {
        Menu menu = menuRepository.get(MenuId.of(menuId));
        return menuAssembler.toMenuData(menu);
    }

    public List<MenuData> tree() {
        List<Menu> menus = menuRepository.all();
        return toTree(menus);
    }

    /**
     * 构建菜单树。参考：<a href="https://mp.weixin.qq.com/s/krp-QdvnKYOYqEgoadI1Zw">Java 编程技巧之数据结构</a>。
     *
     * @param menus 菜单列表
     * @return 菜单树
     */
    private List<MenuData> toTree(List<Menu> menus) {
        //检查列表为空
        if (CollectionUtils.isEmpty(menus)) {
            return new ArrayList<>(0);
        }

        //依次处理菜单
        int size = menus.size();
        List<MenuData> roots = new ArrayList<>(size);
        Map<Long, MenuData> map = new HashMap<>(size);
        for (Menu menu : menus) {
            //赋值菜单对象
            Long menuId = menu.getMenuId().getId();
            MenuData data = map.get(menuId);
            if (Objects.isNull(data)) {
                data = new MenuData();
                data.setChildren(new ArrayList<>());
                map.put(menuId, data);
            }
            data.setMenuId(menu.getMenuId().getId());
            data.setParentId(Objects.nonNull(menu.getParentId()) ? menu.getParentId().getId() : null);
            data.setName(menu.getName());
            data.setIcon(menu.getIcon());
            data.setPath(menu.getPath());
            data.setSortOrder(menu.getSortOrder());

            //根据父标识处理
            Long parentId = Objects.nonNull(menu.getParentId()) ? menu.getParentId().getId() : null;
            if (Objects.nonNull(parentId)) {
                //构建父菜单对象
                MenuData parent = map.get(parentId);
                if (Objects.isNull(parent)) {
                    parent = new MenuData();
                    parent.setMenuId(parentId);
                    parent.setChildren(new ArrayList<>());
                    map.put(parentId, parent);
                }

                //添加子菜单对象
                parent.getChildren().add(data);
            } else {
                //添加根菜单对象
                roots.add(data);
            }
        }

        //返回根菜单列表
        return roots;
    }

    public List<MenuData> getMenuByRoleId(Long roleId) {
        List<Menu> menus = menuRepository.getByRoleId(RoleId.of(roleId));
        return toTree(menus);
    }
}
